using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Xml;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Visuals;
using FilteredStatistics.Common;
using FilteredStatistics.Common.Controller;
using FilteredStatistics.Data;

namespace FilteredStatistics.Controller
{
    public class TreeListColumnManager : INotifyPropertyChanged
    {
        public TreeListColumnManager()
        {
        }

#region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

#endregion

        private void TriggerPropertyChangedEvent(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void UnregisterAllColumns()
        {
            m_InvisibleColumns.Clear();
            m_VisibleColumns.Clear();

            TriggerPropertyChangedEvent("DisplayedColumns");
        }

        public void RegisterColumn(Guid id, IStatisticsProvider provider)
        {
            TreeListColumnInfo columnInfo = GetColumnInfoSafe(id, provider);

            Debug.Assert(columnInfo != null);

            columnInfo.HeaderText = provider.GetColumnHeader(id, m_Activity);

            TriggerPropertyChangedEvent("DisplayedColumns");
        }

        public List<TreeListColumnInfo> GetRegisteredColumns()
        {
            List<TreeListColumnInfo> result = new List<TreeListColumnInfo>();

            result.AddRange(VisibleColumns);
            result.AddRange(HiddenColumns);

            return result;
        }

        public void UpdateRegisteredColumns(List<TreeListColumnInfo> visibleColumns, List<TreeListColumnInfo> hiddenColumns, int fixedColumnCount)
        {
            UnregisterAllColumns();

            m_VisibleColumns.AddRange(visibleColumns);
            m_InvisibleColumns.AddRange(hiddenColumns);
            m_FixedColumnCount = fixedColumnCount;

            TriggerPropertyChangedEvent("DisplayedColumns");
            TriggerPropertyChangedEvent("NumFixedColumns");
        }

        public void SetColumnWidth(Guid id, int columnWidth)
        {
            TreeListColumnInfo columnInfo = FindColumn(id);

            if(columnInfo != null)
            {
                columnInfo.ColumnWidth = columnWidth;
            }

            TriggerPropertyChangedEvent("ColumnWidth");
        }

        public XmlElement SettingsXMLNode
        {
            get { return m_SettingsXmlNode; }
            set { m_SettingsXmlNode = value; }
        }

        public void LoadFromSettings()
        {
            if (SettingsXMLNode != null)
            {
                List<TreeListColumnInfo> tempInvisibleColumns = new List<TreeListColumnInfo>();
                List<TreeListColumnInfo> tempVisibleColumns = new List<TreeListColumnInfo>();
                TreeListColumnInfo.ColumnSortingType sorting = TreeListColumnInfo.ColumnSortingType.Unsorted;
                Guid sortedId = Guid.Empty;

                foreach (XmlNode child in SettingsXMLNode)
                {
                    if (child.Name == Constants.TreeListColumnInfoText)
                    {
                        TreeListColumnInfo columnInfo = null;
                        int width = Constants.DefaultColumnWidth;
                        bool visible = true;

                        foreach (XmlNode attribute in child)
                        {
                            // Value member
                            if (attribute.Name == Constants.ColumnValueMemberText && attribute.ChildNodes.Count == 1)
                            {
                                // Backwards compatibility
                                // FindColumn because we don't want to recreate it
                                columnInfo = FindColumn(FilteredStatisticsStatisticsProvider.Instance.GetGuidFromValueMember(attribute.FirstChild.Value));
                            }
                            else if (attribute.Name == Constants.ColumnGuidText && attribute.ChildNodes.Count == 1)
                            {
                                // FindColumn because we don't want to recreate it
                                columnInfo = FindColumn(new Guid(attribute.FirstChild.Value));
                            }
                            // Width
                            else if (attribute.Name == Constants.ColumnWidthText && attribute.ChildNodes.Count == 1)
                            {
                                width = int.Parse(attribute.FirstChild.Value);
                            }
                            // Visible
                            else if (attribute.Name == Constants.ColumnVisibilityText && attribute.ChildNodes.Count == 1)
                            {
                                visible = bool.Parse(attribute.FirstChild.Value);
                            }
                        }

                        // Update our info with the loaded data
                        if (columnInfo != null)
                        {
                            TreeListColumnInfo newColumn = new TreeListColumnInfo(columnInfo.Id, columnInfo.Provider,
                                                                                  width, columnInfo.HeaderText, visible);

                            // Remove from our current list
                            if (columnInfo.IsVisible)
                            {
                                m_VisibleColumns.Remove(columnInfo);
                            }
                            else
                            {
                                m_InvisibleColumns.Remove(columnInfo);
                            }

                            // Add to our temporary list
                            if (newColumn.IsVisible)
                            {
                                tempVisibleColumns.Add(newColumn);
                            }
                            else
                            {
                                tempInvisibleColumns.Add(newColumn);
                            }
                        }
                    }
                    else if (child.Name == Constants.FixedColumnCountText && child.ChildNodes.Count == 1)
                    {
                        // Fixed column count
                        m_FixedColumnCount = int.Parse(child.FirstChild.Value);
                    }
                    else if (child.Name == Constants.SortedColumnText && child.ChildNodes.Count == 2)
                    {
                        foreach (XmlNode subChild in child.ChildNodes)
                        {
                            if (subChild.Name == Constants.SortedColumnIdText && subChild.ChildNodes.Count == 1)
                            {
                                sortedId = new Guid(subChild.FirstChild.Value);
                            }
                            else if (subChild.Name == Constants.SortedColumnTypeText && subChild.ChildNodes.Count == 1)
                            {
                                sorting = (TreeListColumnInfo.ColumnSortingType)int.Parse(subChild.FirstChild.Value);
                            }
                        }
                    }
                }

                // Add what's left of the registered columns to the right lists, at the end
                tempVisibleColumns.AddRange(m_VisibleColumns);
                tempInvisibleColumns.AddRange(m_InvisibleColumns);

                // Officialize our lists
                m_VisibleColumns.Clear();
                m_VisibleColumns.AddRange(tempVisibleColumns);
                m_InvisibleColumns.Clear();
                m_InvisibleColumns.AddRange(tempInvisibleColumns);

                TriggerPropertyChangedEvent("DisplayedColumns");

                if (sortedId != Guid.Empty && sorting != TreeListColumnInfo.ColumnSortingType.Unsorted)
                {
                    SetColumnSorting(sortedId, sorting);
                }
            }

            SettingsXMLNode = null;
        }

        public void SaveToSettings(XmlDocument xmlDoc, XmlNode pluginNode, String childNodeName)
        {
            XmlNode masterNode = xmlDoc.CreateElement(childNodeName);
            pluginNode.AppendChild(masterNode);

            // Make sure we use the last settings
            if (SettingsXMLNode != null)
            {
                LoadFromSettings();
            }

            foreach (TreeListColumnInfo columnInfo in VisibleColumns)
            {
                SaveSingleNode(columnInfo, xmlDoc, masterNode);
            }

            foreach (TreeListColumnInfo columnInfo in HiddenColumns)
            {
                SaveSingleNode(columnInfo, xmlDoc, masterNode);
            }

            // Fixed column
            XmlNode dataNode;
            dataNode = xmlDoc.CreateElement(Constants.FixedColumnCountText);
            masterNode.AppendChild(dataNode);
            dataNode.AppendChild(xmlDoc.CreateTextNode(FixedColumnCount.ToString()));

            // Sorted column
            if(SortedColumnInfo != null)
            {
                XmlNode subChildNode;

                dataNode = xmlDoc.CreateElement(Constants.SortedColumnText);
                masterNode.AppendChild(dataNode);

                // Column id
                subChildNode = xmlDoc.CreateElement(Constants.SortedColumnIdText);
                dataNode.AppendChild(subChildNode);
                subChildNode.AppendChild(xmlDoc.CreateTextNode(SortedColumnInfo.Id.ToString()));

                // Sorting order
                subChildNode = xmlDoc.CreateElement(Constants.SortedColumnTypeText);
                dataNode.AppendChild(subChildNode);
                subChildNode.AppendChild(xmlDoc.CreateTextNode(((int)SortedColumnInfo.Sorting).ToString()));
            }
        }

        private void SaveSingleNode(TreeListColumnInfo columnInfo, XmlDocument xmlDoc, XmlNode masterNode)
        {
            XmlNode child, dataNode;

            child = xmlDoc.CreateElement(Constants.TreeListColumnInfoText);
            masterNode.AppendChild(child);

            // Value member
            dataNode = xmlDoc.CreateElement(Constants.ColumnGuidText);
            child.AppendChild(dataNode);
            dataNode.AppendChild(xmlDoc.CreateTextNode(columnInfo.Id.ToString()));

            // Width
            dataNode = xmlDoc.CreateElement(Constants.ColumnWidthText);
            child.AppendChild(dataNode);
            dataNode.AppendChild(xmlDoc.CreateTextNode(columnInfo.ColumnWidth.ToString()));

            // Visible
            dataNode = xmlDoc.CreateElement(Constants.ColumnVisibilityText);
            child.AppendChild(dataNode);
            dataNode.AppendChild(xmlDoc.CreateTextNode(columnInfo.IsVisible.ToString()));
        }

        private TreeListColumnInfo GetColumnInfoSafe(Guid id, IStatisticsProvider provider)
        {
            TreeListColumnInfo columnInfo = FindColumn(id);

            if (columnInfo == null)
            {
                columnInfo = new TreeListColumnInfo(id, provider);

                m_VisibleColumns.Add(columnInfo);
            }

            return columnInfo;
        }

        public TreeListColumnInfo FindColumn(Guid id)
        {
            foreach (TreeListColumnInfo columnInfo in m_VisibleColumns)
            {
                if (columnInfo.Id == id)
                {
                    return columnInfo;
                }
            }

            foreach (TreeListColumnInfo columnInfo in m_InvisibleColumns)
            {
                if (columnInfo.Id == id)
                {
                    return columnInfo;
                }
            }

            return null;
        }

        public void SetColumnSorting(Guid id, TreeListColumnInfo.ColumnSortingType sorting)
        {
            TreeListColumnInfo newSortedColumn = FindColumn(id);

            if (newSortedColumn != null)
            {
                if (m_SortedColumn != null)
                {
                    m_SortedColumn.Sorting = TreeListColumnInfo.ColumnSortingType.Unsorted;
                }

                newSortedColumn.Sorting = sorting;
                m_SortedColumn = newSortedColumn;
            }

            TriggerPropertyChangedEvent("SortColumn");
        }

        public IActivity Activity
        {
            set
            {
                m_Activity = value;
            }
        }

        public TreeListColumnInfo SortedColumnInfo
        {
            get { return m_SortedColumn; }
        }

        public List<TreeListColumnInfo> VisibleColumns
        {
            get { return m_VisibleColumns; }
        }

        public List<TreeListColumnInfo> HiddenColumns
        {
            get { return m_InvisibleColumns; }
        }

        public int FixedColumnCount
        {
            get { return m_FixedColumnCount; }
        }

        private IActivity m_Activity = null;
        private List<TreeListColumnInfo> m_InvisibleColumns = new List<TreeListColumnInfo>();
        private List<TreeListColumnInfo> m_VisibleColumns = new List<TreeListColumnInfo>();
        private TreeListColumnInfo m_SortedColumn = null;
        private XmlElement m_SettingsXmlNode = null;
        private int m_FixedColumnCount = 0;
    }
}
